﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml;

namespace ClassLibrary
{

    /* Chromosome and genes link table */
    public class ChromosomeGeneLink
    {
        public Dictionary<string, int[]> link;        
        public string path { set; get; }
        public string type { set; get; }
        public string name { set; get; }
        public string path_to_gene_table { set; get; }
        public string path_to_chromosome_table { set; get; }
        public int chromosome_pairs_amount { set; get; }
        public bool available { set; get; }


        /* constructor */
        public ChromosomeGeneLink(string path_to_chromosome_gene_link)
        {
            path = path_to_chromosome_gene_link;

            // open XML file            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(File.ReadAllText(path_to_chromosome_gene_link));

            // reading gene_table's attributes
            type = doc.DocumentElement.GetAttribute("Type");
            name = doc.DocumentElement.GetAttribute("Name");
            path_to_chromosome_table = doc.DocumentElement.GetAttribute("PathToChromosomeTable");
            path_to_gene_table = doc.DocumentElement.GetAttribute("PathToGeneTable");
            chromosome_pairs_amount = Convert.ToInt32(doc.DocumentElement.GetAttribute("ChromosomePairsAmount"));


            // reading all data about link
            if (chromosome_pairs_amount != 0)
            {
                // reading "chromosomes" node
                link = new Dictionary<string,int[]>();                
                foreach (XmlNode xml_node in doc.GetElementsByTagName("chromosome"))
                {
                    if (xml_node.Attributes[1].Value == "0")
                    {
                        continue;
                    }
                    else
                    {
                        string id = xml_node.Attributes[0].Value;
                        link.Add(id, new int[Convert.ToInt32(xml_node.Attributes[1].Value)]);
                        //reading gene_categories
                        int j = -1;
                        foreach (string str_allele_id in xml_node.ChildNodes[0].InnerText.Split(new char[]{' '},StringSplitOptions.RemoveEmptyEntries))
                        {
                            j++;                            
                            link[id][j] = Convert.ToInt32(str_allele_id);
                        }
                    }
                }
            }
        }
    }


    /* Geneome and phenotype link table */
    public class GenomePhenotypeLink
    {
        // map charcteristic_name ---> class_function: value(genome)
        public Dictionary<string, CharacteristicLink> link = new Dictionary<string, CharacteristicLink>();

        public string path { set; get; }
        public string type { set; get; }
        public string name { set; get; }

        public string path_to_gene_table { set; get; }
        public string path_to_phenotype_table { set; get; }

        public int characteristics_amount { set; get; }
        public bool available { set; get; }

        /* constructor */
        public GenomePhenotypeLink(string path_to_genome_phenotype_link)
        {
            path = path_to_genome_phenotype_link;

            // open XML file            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(File.ReadAllText(path_to_genome_phenotype_link));


            // reading gene_table's attributes
            type = doc.DocumentElement.GetAttribute("Type");
            name = doc.DocumentElement.GetAttribute("Name");

            path_to_gene_table = doc.DocumentElement.GetAttribute("PathToGeneTable");
            path_to_phenotype_table = doc.DocumentElement.GetAttribute("PathToPhenotypeTable");

            characteristics_amount = Convert.ToInt32(doc.DocumentElement.GetAttribute("CharacteristicsAmount"));


            // reading all data about link
            if (characteristics_amount != 0)
            {
                // reading "characteristic" nodes

                int ch_id;
                string ch_name;
                CharacteristicLink characteristic_link;

                XmlNodeList xml_node_list = doc.GetElementsByTagName("characteristics")[0].ChildNodes;

                foreach (XmlNode xml_node in xml_node_list)
                {
                    // getting characteristic name
                    ch_id = Convert.ToInt32(xml_node.Attributes[0].Value);
                    ch_name = xml_node.Attributes[1].Value;

                    // setting characteristic from file
                    characteristic_link = new CharacteristicLink(ch_id, ch_name);                    

                    // reading "value" nodes
                    int value;

                    XmlNodeList value_xml_nodes = xml_node.SelectSingleNode("values").SelectNodes("value");

                    foreach (XmlNode single_val_xml_node in value_xml_nodes)
                    {
                        value = Convert.ToInt32(single_val_xml_node.Attributes["Number"].Value);

                        XmlNodeList gene_xml_nodes = single_val_xml_node.SelectSingleNode("Genes").SelectNodes("Gene");

                        int gene_id;
                        string gene_name;
                        int gene_amount;

                        // reading "gene" nodes
                        foreach (XmlNode single_gene_xml_node in gene_xml_nodes)
                        {
                           
                            gene_id = Convert.ToInt32(single_gene_xml_node.Attributes[0].Value);
                            gene_name = single_gene_xml_node.Attributes[1].Value;
                            gene_amount = Convert.ToInt32(single_gene_xml_node.Attributes[2].Value);
                            
                            string combination_piece;
                            
                            // getting a piece of gene combination in string format
                            for (int i = 0; i < single_gene_xml_node.Attributes.Count - 3; i++)
                            {
                                // number of the allele
                                combination_piece = i.ToString();

                                // gene's name
                                combination_piece += gene_name;

                                // amount of the alleles
                                combination_piece += single_gene_xml_node.Attributes[i + 3].Value;

                                // adding piece of combination
                                characteristic_link.add(value, combination_piece);
                            }
                        }
                    }
                    link.Add(ch_name, characteristic_link);
                }
            }
        }
    }


    /*
     *  auxilary class for storing data
     *  about characteristic's degrees and its gene combinations
     */
    public class CharacteristicLink
    {
        public int ch_id { set; get; }
        public string ch_name { set; get; }
        public Dictionary<int, List<string>> genome_charactrectistic_map = new Dictionary<int,List<string>>();

        public CharacteristicLink(int id, string name)
        {
            ch_id = id;
            ch_name = name;
        }

        // adds allels to dependency
        public void add(int degree, string allels)
        {
            if (!genome_charactrectistic_map.ContainsKey(degree))
            {
                genome_charactrectistic_map.Add(degree, new List<string>());
            }
            genome_charactrectistic_map[degree].Add(allels);
        }
    }


    /*
     * Class for describing mapping of
     * character space into event space
     */
    public class NaturalSelectionLink
    {
        public string path { set; get; }
        public string type { set; get; }
        public string name { set; get; }
        public string path_to_phenotype_table { set; get; }


        // natural_selection as fiunction : characteristic_vector --> event_vector
        public int characteristic_amount { set; get; }
        public EventLinkNode natural_selection_link;

        public bool available { set; get; }

        /* constructor */
        public NaturalSelectionLink(string path_to_natural_selection_link)
        {
            path = path_to_natural_selection_link;

            // open XML file            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(File.ReadAllText(path_to_natural_selection_link));

            // reading gene_table's attributes
            type = doc.DocumentElement.GetAttribute("Type");
            name = doc.DocumentElement.GetAttribute("Name");
            path_to_phenotype_table = doc.DocumentElement.GetAttribute("PathToPhenotypeTable");
            characteristic_amount = Convert.ToInt32(doc.DocumentElement.GetAttribute("CharacteristicsAmount"));


            if (characteristic_amount != 0)
            {
                // creating tree for natural_selection_link

                string[] characteristic_names;
                string[] str_characteristic_diapasons;
                int[] int_characteristic_diapasons;

                characteristic_names = doc.GetElementsByTagName("characteristic_names")[0].InnerText.Split(new char[] {' '},StringSplitOptions.RemoveEmptyEntries);
                str_characteristic_diapasons = doc.GetElementsByTagName("characteristic_value_diapasons")[0].InnerText.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                int_characteristic_diapasons = new int[characteristic_amount];
                for (int i = 0; i < characteristic_amount; i++)
                {
                    // + 1 for zero value
                    int_characteristic_diapasons[i] = Convert.ToInt32(str_characteristic_diapasons[i]) + 1;
                }

                natural_selection_link = new EventLinkNode(int_characteristic_diapasons);


                // loading tree for natural_selection_link

                foreach (XmlNode xml_node in doc.GetElementsByTagName("event"))
                {
                    // getting event

                    int current_event_id = Convert.ToInt32(xml_node.Attributes[0].Value);
                    string current_event_name = xml_node.Attributes[1].Value;

                    Event current_event = EventTable.getEvent(current_event_id);


                    // setting values for natural selectoin's end nodes                    


                    // reading characteristic vectors

                    XmlNode child_xml_node = xml_node.ChildNodes[0];

                    char[] delimiters = new char[] { '\t', '\n', '\r', ';' };
                    string[] str_characterisric_vector_massive = child_xml_node.InnerText.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);

                    int characterisric_vectors_amount = str_characterisric_vector_massive.Count();
                    int[][] int_characterisric_vector_massive = new int[characterisric_vectors_amount][];

                    for (int i = 0; i < characterisric_vectors_amount; i++)
                    {
                        string[] str_characteristic_vector = str_characterisric_vector_massive[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        int[] int_characteristic_vector = new int[characteristic_amount];

                        for (int j = 0; j < characteristic_amount; j++)
                        {
                            int_characteristic_vector[j] = Convert.ToInt32(str_characteristic_vector[j]);
                        }
                        int_characterisric_vector_massive[i] = int_characteristic_vector;
                    }


                    // setting end_nodes according to characteristic vectors
                    for (int i = 0; i < characterisric_vectors_amount; i++)
                    {
                        natural_selection_link.setEndNode(int_characterisric_vector_massive[i], current_event);
                    }
                }
            }
        }


        /*           
         * Method
         * Args: int[] - characteristic vector
         * Does: maps chararacteristic vector into event vector
         * Returns: event vector
         */
        public Event mapCharacteristicVectorIntoEventVector(int[] characteristic_vector)
        {
            return natural_selection_link.getEndNode(characteristic_vector);
        }
    }


    /*
     * Class for describing model's component
     * responsible for reproduction
     */
    public class ReproductionLink
    {
        public string path { set; get; }
        public string type { set; get; }
        public string name { set; get; }
        public string path_to_phenotype_table { set; get; }
        public string path_to_natsel_link { set; get; }


        public int characteristic_amount { set; get; }


        // reproduction
        public int reproduction_events_amount { set; get; }
        public Dictionary<IntMassive, int> reproduction = new Dictionary<IntMassive, int>();


        public bool available { set; get; }

        /* constructor */
        public ReproductionLink(string path_to_reproduction_link)
        {
            path = path_to_reproduction_link;

            // open XML file            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(File.ReadAllText(path_to_reproduction_link));

            // reading gene_table's attributes
            type = doc.DocumentElement.GetAttribute("Type");
            name = doc.DocumentElement.GetAttribute("Name");
            path_to_phenotype_table = doc.DocumentElement.GetAttribute("PathToPhenotypeTable");
            path_to_natsel_link = doc.DocumentElement.GetAttribute("PathToNaturalSelectionLink");
            characteristic_amount = Convert.ToInt32(doc.DocumentElement.GetAttribute("CharacteristicsAmount"));
            reproduction_events_amount = Convert.ToInt32(doc.DocumentElement.GetAttribute("ReproductionEventsAmount"));


            if (reproduction_events_amount != 0)
            {
                // loading map for reproduction

                foreach (XmlNode xml_node in doc.GetElementsByTagName("reproduction_event"))
                {
                    // getting reproduction event description

                    int current_event_id = Convert.ToInt32(xml_node.Attributes[0].Value);
                    string current_event_name = xml_node.Attributes[1].Value;
                    int children_amount = Convert.ToInt32(xml_node.Attributes[2].Value);                


                    // reading characteristic vectors

                    XmlNode child_xml_node = xml_node.ChildNodes[0];

                    char[] delimiters = new char[] { '\t', '\n', '\r', ';' };
                    string[] str_characterisric_vector_massive = child_xml_node.InnerText.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);

                    int characterisric_vectors_amount = str_characterisric_vector_massive.Count();

                    for (int i = 0; i < characterisric_vectors_amount; i++)
                    {
                        string[] str_characteristic_vector = str_characterisric_vector_massive[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        IntMassive int_massive = new IntMassive(characteristic_amount);

                        for (int j = 0; j < characteristic_amount; j++)
                        {
                            int_massive.massive[j] = Convert.ToInt32(str_characteristic_vector[j]);
                        }
                        reproduction.Add(int_massive, children_amount);
                    }
                }
            }
        }
    }
}